﻿using System;

using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
using System.Net;
using cc2530.Lib;

namespace cc2530.Lib.device
{


    /// <summary>
    /// 网络设备
    /// </summary>
     public class TapWin
     {

        #region 私有

         static int TapWinCount = 0;

         string adapterkeystring;

        const string UsermodeDeviceSpace = "\\\\.\\Global\\";
        private IntPtr m_hNet;
        private SafeFileHandle m_hSafeNet;

        const int FILE_ATTRIBUTE_SYSTEM = 0x4;
        const int FILE_FLAG_OVERLAPPED = 0x40000000;
        
        [DllImport("Kernel32.dll", /* ExactSpelling = true, */ SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr CreateFile(
            string filename,
            [MarshalAs(UnmanagedType.U4)]FileAccess fileaccess,
            [MarshalAs(UnmanagedType.U4)]FileShare fileshare,
            int securityattributes,
            [MarshalAs(UnmanagedType.U4)]FileMode creationdisposition,
            int flags,
            IntPtr template);
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
            IntPtr lpInBuffer, uint nInBufferSize,
            IntPtr lpOutBuffer, uint nOutBufferSize,
            out int lpBytesReturned, IntPtr lpOverlapped);
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);

        private const uint METHOD_BUFFERED = 0;
        private const uint FILE_ANY_ACCESS = 0;
        private const uint FILE_DEVICE_UNKNOWN = 0x00000022;
        public static int BUF_SIZE = 2048;

        static FileStream Tap;
        static EventWaitHandle WaitReadEvent;
        static EventWaitHandle WaitWriteEvent;
        AsyncCallback m_asyReadCallBack;
        AsyncCallback m_asyWriteCallBack;
        IAsyncResult m_asyResult;

		string m_strGuid;

        RingBuf m_ReadRingBuf = new RingBuf(50, BUF_SIZE);
        byte[] m_nReadBuf = new byte[BUF_SIZE];
        byte[] m_nWriteBuf = new byte[BUF_SIZE];
        static Semaphore m_semRead = new Semaphore(0, 32);
		Thread m_ReadDataThread;
        private volatile bool _shouldStopReadData = false;
        private volatile bool _isopen = false;

        Thread receiveThread;

#endregion



        #region 事件
        public event Ulitily.OnTransferHandler onTapReceiverHandeler;
        #endregion

        #region public Static varibles

        /// <summary>
         /// 默认网卡设备
         /// </summary>
        public const string DEFAULT_ADAPTTER_KEY = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";

        #endregion

        #region Private Function

        /// <summary>
         /// 获得网络 Guid 串
         /// </summary>
         /// <returns></returns>
        string GetGuid() {
            return this.GetGuid(adapterkeystring);
        }

         /// <summary>
         /// 重载，获得网络 Guid 串
         /// </summary>
         /// <param name="key">入参，网络adapter key 值</param>
         /// <returns></returns>
        string GetGuid( string key)
        {

            string devGuid = "";
            try
            {
                string AdapterKey = key;// "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";//{4d36e972-e325-11ce-bfc1-08002be10318}

                RegistryKey regAdapters = Registry.LocalMachine.OpenSubKey(AdapterKey, true);
                string[] keyNames = regAdapters.GetSubKeyNames();

                foreach (string x in keyNames)
                {
                    RegistryKey regAdapter = regAdapters.OpenSubKey(x);
                    object id = regAdapter.GetValue("ComponentId");
                    if (id != null && id.ToString() == "tap0901")
                    {
                        devGuid = regAdapter.GetValue("NetCfgInstanceId").ToString();
                        break;
                    }
                }

                regAdapters.Close();
                return devGuid;
            }
            catch (System.Exception ex)
            {
                //throw (ex);
                // return null;
                if (devGuid != "") return devGuid;
                else throw (ex);

            }


        }

        #endregion


        #region Public Function ,Properties

         /// <summary>
         /// 设备guid 只读
         /// </summary>
        public string Tap_Guid
		{
            get { return m_strGuid; }
		}

        public bool IsOpen { get { return _isopen; } }

         /// <summary>
         /// 打开设备
         /// </summary>
         /// <returns></returns>
        public bool TapWin_Open()
        {
            string guid = GetGuid();
            if (guid.Length == 0)
            {
                return false;
            }
			m_strGuid = guid;
            m_hNet = CreateFile(UsermodeDeviceSpace + guid + ".tap", FileAccess.ReadWrite, FileShare.ReadWrite, 0, FileMode.Open, 
                    FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, IntPtr.Zero);
            m_hSafeNet = new SafeFileHandle(m_hNet, true);
            if (m_hSafeNet.IsInvalid)
            {
                return false;
            }

            int len;
            bool ret;
            IntPtr pstatus = Marshal.AllocHGlobal(4);
            Marshal.WriteInt32(pstatus, 1);
            // 打开网络
            ret = DeviceIoControl(m_hNet, TAP_CONTROL_CODE(6, METHOD_BUFFERED), pstatus, 4, pstatus, 4, out len, IntPtr.Zero);
            if (ret == false)
            {
                CloseHandle(m_hNet);
            }

            Tap = new FileStream(m_hSafeNet, FileAccess.ReadWrite, 2048, true);

            WaitReadEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
            if (m_asyReadCallBack == null)
            {
                m_asyReadCallBack = new AsyncCallback(ReadDataCallBack);
            }
            m_ReadDataThread = new Thread(ReadDataThread);
            m_ReadDataThread.Name = "Tab-"+TapWin.TapWinCount +" :ReadDataThread";
            m_ReadDataThread.Start();
           

            WaitWriteEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
            if (m_asyWriteCallBack == null)
            {
                m_asyWriteCallBack = new AsyncCallback(WriteDataCallBack);
            }


            //线程处理数据
            receiveThread = new Thread(ReceiveThread);
            receiveThread.Name = "Tab-" + TapWin.TapWinCount + " :ReceiveThread";
            //receiveThread.Start();
            

            TapWin.TapWinCount ++;
            _isopen = true;
            return true;
        }

         /// <summary>
         /// 关闭设备
         /// </summary>
        public void TapWin_Close()
        {
            _shouldStopReadData = true;
            if (this.m_ReadDataThread != null) { this.m_ReadDataThread.Abort(); this.m_ReadDataThread = null; }
            if (this.receiveThread != null) { this.receiveThread.Abort(); this.receiveThread = null; }
            WaitReadEvent.Set();

            _isopen = false;
        }


         /// <summary>
         /// 写数据
         /// </summary>
         /// <param name="buf">入参,buffer</param>
         /// <param name="len">入参,buffer 长度</param>
         /// <returns></returns>
        public int WriteData(byte[] buf, int len)
        {
            if (len > BUF_SIZE)
                return len;
            Array.Copy(buf, m_nWriteBuf, len);
            m_asyResult = Tap.BeginWrite(m_nWriteBuf, 0, len, m_asyWriteCallBack, 0);
            Tap.Flush();                // 这个必须要增加，不然不会立刻把数据写到网络中，会造成超时
            WaitWriteEvent.WaitOne();
            return len;
        }


         /// <summary>
         /// 读数据
         /// </summary>
         /// <param name="pBuf">出参,buffer</param>
         /// <param name="ReadSize">入参,要读数据长度</param>
         /// <returns>读取长度</returns>
        public int ReadData(byte[] pBuf, int ReadSize)
        {
            m_semRead.WaitOne();
            int len;
            len = m_ReadRingBuf.ReadBuf(pBuf, ReadSize);
            return len;
        }


        public byte[] GetMac() { byte[] m = new byte[6]; this.GetMac(m); return m; }

         /// <summary>
         /// 设备mac
         /// </summary>
         /// <param name="pMac">出参</param>
        public void GetMac(byte[] pMac)
        {
            int len = 0;
            bool ret;
            if (!m_hSafeNet.IsInvalid)
            {
                IntPtr mac = Marshal.AllocHGlobal(6);
                ret = DeviceIoControl(m_hNet, TAP_CONTROL_CODE(1, METHOD_BUFFERED), mac, 6, mac, 6, out len, IntPtr.Zero);
                if (!ret)
                {
                    pMac[0] = 0;
                    pMac[1] = 0;
                    pMac[2] = 0;
                    pMac[3] = 0;
                    pMac[4] = 0;
                    pMac[5] = 0;
                }
                else
                {
                    //Marshal.
                    pMac[0] = Marshal.ReadByte(mac + 0);
                    pMac[1] = Marshal.ReadByte(mac + 1);
                    pMac[2] = Marshal.ReadByte(mac + 2);
                    pMac[3] = Marshal.ReadByte(mac + 3);
                    pMac[4] = Marshal.ReadByte(mac + 4);
                    pMac[5] = Marshal.ReadByte(mac + 5);
                }
            }
            else
            {
                pMac[0] = 0;
                pMac[1] = 0;
                pMac[2] = 0;
                pMac[3] = 0;
                pMac[4] = 0;
                pMac[5] = 0;
            }
        }


        //public IPAddress GetIpaddress()

        #endregion


        #region 私有函数

        #region Read thread


        /// <summary>
         /// 读网络数据线程
         /// </summary>
        void ReadDataThread()
        {
            object read_state = new int();
            while (!_shouldStopReadData)
            {
                m_asyResult = Tap.BeginRead(m_nReadBuf, 0, BUF_SIZE, m_asyReadCallBack, read_state);
                WaitReadEvent.WaitOne();
            }
            
        }
        private static uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)
        {
            return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method);
        }

        static uint TAP_CONTROL_CODE(uint request, uint method)
        {
            return CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS);
        }

        private void ReadDataCallBack(IAsyncResult asyncResult)
        {
            try
            {
                int BytesRead;
                BytesRead = Tap.EndRead(asyncResult);
                m_ReadRingBuf.WriteBuf(m_nReadBuf, BytesRead);
                Console.WriteLine("TapWin get {0} bytes",BytesRead);
                WaitReadEvent.Set();
                m_semRead.Release(1);
            }
            catch (Exception e)
            {
                Console.WriteLine("ReadDataCallBack accurt exception:" + e.ToString());
            }
        }

        /// <summary>
        /// 接收线程
        /// </summary>
        void ReceiveThread() {
            int re = 0;
            TransationBase args = new TransationBase();
            args.data = new byte[TapWin.BUF_SIZE];
            args.datalen = 0;
            while (true) {
                args.datalen = this.ReadData(args.data, args.data.Length);
                if (re > 0 && this.onTapReceiverHandeler != null)
                {
                    this.onTapReceiverHandeler(this, args);
                }
            }
        }

        #endregion


        private void WriteDataCallBack(IAsyncResult asyncResult)
        {
            Tap.EndWrite(asyncResult);
            WaitWriteEvent.Set();
        }


        #endregion



         #region 构造
        public TapWin(string adkey) {
            adapterkeystring = adkey;
        }

        public TapWin() {
            adapterkeystring = TapWin.DEFAULT_ADAPTTER_KEY;
        }
        #endregion

     }


}
